/**
 * Copyright 2009 - ChunMap
 * 
 * 这个文件是ChunMap项目的一部分。
 * ChunMap是开源软件。
 * 你可以自由复制、传播本项目的下载包文件，但必须保持其完整性。
 * 我们不用对使用中的风险及由此造成的损失承担任何责任。
 * 详细情况请见《ChunMap许可协议》。

 * 想了解更多有关ChunMap的信息，请访问http://code.google.com/p/chunmap/
 */
package chunmap.orm.sql;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import chunmap.orm.connect.DBUtils;
import chunmap.util.ChunDaoException;
import chunmap.util.Function;
import chunmap.util.Logger;

/**
 * @author yangjiandong
 * 
 */
public class Sql {
	private static final Logger Log = new Logger(Logger.Info, Sql.class
			.getName());
	public final static int UpdateExecute = 1;
	public final static int NoneExecute = 0;
	public final static int QueryExecute = 2;

	private String sqlTemp;
	private int type;
	private Object result;
	private List<SqlParameter> parameters = new ArrayList<SqlParameter>();

	public Sql(String sqlTemp, int type) {
		super();
		this.sqlTemp = sqlTemp;
		this.type = type;
	}

	public void addParameter(SqlParameter param) {
		parameters.add(param);
	}

	public Object getResult() {
		return result;
	}

	public void execute(Connection conn, Function callback) {
		try {
			executeSql(conn, callback);
		} catch (SQLException e) {
			try {
				if (conn.getAutoCommit() == false) {
					conn.rollback();
				}
			} catch (SQLException e1) {
				throw new ChunDaoException(e1);
			}
			try {
				conn.close();
			} catch (SQLException e1) {
				e1.printStackTrace();
			}
			throw new ChunDaoException(e);
		}
	}

	private void executeSql(Connection con, Function callback)
			throws SQLException {
		Statement ps = getStatement(con);
		exeSomeStatement(ps);

		if (type == QueryExecute && result == null) {
			System.out.println("resultSet is null");
		}
		if (callback != null) {
			callback.call(result);
		}
		if (result instanceof ResultSet) {
			DBUtils.colseResultSet((ResultSet) result);
		}
		DBUtils.colseStatement(ps);
	}

	private void exeSomeStatement(Statement ps) throws SQLException {
		if (ps instanceof PreparedStatement) {
			PreparedStatement p = (PreparedStatement) ps;
			Log.log(Logger.Info, sqlTemp);

			exePreparedStatement(p);
		} else {
			Log.log(Logger.Info, sqlTemp);

			exeStatement(ps);
		}
	}

	private void exeStatement(Statement ps) throws SQLException {
		switch (type) {
		case NoneExecute:
			result = ps.execute(sqlTemp);
			break;
		case UpdateExecute:
			result = ps.executeUpdate(sqlTemp);
			break;
		case QueryExecute:
			result = ps.executeQuery(sqlTemp);
			break;
		default:
			throw new ChunDaoException("unknow sqlType");
		}
	}

	private void exePreparedStatement(PreparedStatement ps) throws SQLException {
		switch (type) {
		case NoneExecute:
			result = ps.execute();
			break;
		case UpdateExecute:
			result = ps.executeUpdate();
			break;
		case QueryExecute:
			result = ps.executeQuery();
			break;
		default:
			throw new ChunDaoException("unknow sqlType");
		}
	}

	private Statement getStatement(Connection con) throws SQLException {

		if (parameters.size() == 0) {
			return con.createStatement();
		}
		PreparedStatement ps = con.prepareStatement(sqlTemp);
		for (SqlParameter p : parameters) {
			p.setPreparedParmeter(ps);
		}
		return ps;
	}

	@Override
	public String toString() {
		Collections.sort(parameters);

		String s = sqlTemp;
		for (SqlParameter p : parameters) {
			s = s.replaceFirst("\\?", p.toString());
		}
		return s;
	}

	public void addAllSqlParameters(List<SqlParameter> sqlParameters) {
		for (SqlParameter p : sqlParameters) {
			addParameter(p);
		}
	}
}
